﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CsvHelper;
using CsvHelper.Excel;
using NiuX.Utils.Consts;
using NiuX.Utils.Extensions;
using NiuX.Utils.Utils;

// ReSharper disable once CheckNamespace
namespace NiuX.Csv.Utils;

/// <summary>
/// Excel 助手
/// </summary>
public static partial class CsvHelperUtility
{
    public static string DefaultExcelPostfix { get; set; } = DefaultPostfixConsts.Excel;

    #region ReadExcelAsList

    public static List<T> ReadExcelAsList<T>(string path) => ReadExcel<T, List<T>>(path, x => x.ToList());

    public static List<T> ReadExcelAsList<T>(string path, string sheetName) => ReadExcel<T, List<T>>(path, sheetName, x => x.ToList());

    public static List<T> ReadExcelAsList<T>(string path, CultureInfo culture) => ReadExcel<T, List<T>>(path, culture, x => x.ToList());

    public static List<T> ReadExcelAsList<T>(string path, string sheetName, CultureInfo culture) => ReadExcel<T, List<T>>(path, sheetName, culture, x => x.ToList());

    public static List<T> ReadExcelAsList<T>(Stream stream, CultureInfo culture, bool leaveOpen = false) => ReadExcel<T, List<T>>(stream, culture, leaveOpen, x => x.ToList());

    public static List<T> ReadExcelAsList<T>(Stream stream, string sheetName, CultureInfo culture, bool leaveOpen = false) => ReadExcel<T, List<T>>(stream, sheetName, culture, leaveOpen, x => x.ToList());

    #endregion

    #region ReadExcelAsArray

    public static T[] ReadExcelAsArray<T>(string path) => ReadExcel<T, T[]>(path, x => x.ToArray());

    public static T[] ReadExcelAsArray<T>(string path, string sheetName) => ReadExcel<T, T[]>(path, sheetName, x => x.ToArray());

    public static T[] ReadExcelAsArray<T>(string path, CultureInfo culture) => ReadExcel<T, T[]>(path, culture, x => x.ToArray());

    public static T[] ReadExcelAsArray<T>(string path, string sheetName, CultureInfo culture) => ReadExcel<T, T[]>(path, sheetName, culture, x => x.ToArray());

    public static T[] ReadExcelAsArray<T>(Stream stream, CultureInfo culture, bool leaveOpen = false) => ReadExcel<T, T[]>(stream, culture, leaveOpen, x => x.ToArray());

    public static T[] ReadExcelAsArray<T>(Stream stream, string sheetName, CultureInfo culture, bool leaveOpen = false) => ReadExcel<T, T[]>(stream, sheetName, culture, leaveOpen, x => x.ToArray());

    #endregion

    #region ReadExcel

    private static TReturn ReadExcel<TItem, TReturn>(string path, Func<IEnumerable<TItem>, TReturn> func) => ReadExcel(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix)), func);

    private static TReturn ReadExcel<TItem, TReturn>(string path, string sheetName, Func<IEnumerable<TItem>, TReturn> func) => ReadExcel(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix), sheetName), func);

    private static TReturn ReadExcel<TItem, TReturn>(string path, CultureInfo culture, Func<IEnumerable<TItem>, TReturn> func) => ReadExcel(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix), culture), func);

    private static TReturn ReadExcel<TItem, TReturn>(string path, string sheetName, CultureInfo culture, Func<IEnumerable<TItem>, TReturn> func) => ReadExcel(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix), sheetName, culture), func);

    private static TReturn ReadExcel<TItem, TReturn>(Stream stream, CultureInfo culture, bool leaveOpen, Func<IEnumerable<TItem>, TReturn> func) => ReadExcel(new ExcelParser(stream, culture, leaveOpen), func);

    private static TReturn ReadExcel<TItem, TReturn>(Stream stream, string sheetName, CultureInfo culture, bool leaveOpen, Func<IEnumerable<TItem>, TReturn> func) => ReadExcel(new ExcelParser(stream, sheetName, culture, leaveOpen), func);

    private static TReturn ReadExcel<TItem, TReturn>(ExcelParser excelParser, Func<IEnumerable<TItem>, TReturn> func)
    {
        using var reader = excelParser;
        using var csv = new CsvReader(reader);
        return func(csv.GetRecords<TItem>());
    }

    #endregion

    #region ReadExcelAsListAsync
    public static async Task<List<T>> ReadExcelAsListAsync<T>(string path) => await ReadExcelAsync<T, List<T>>(path, x => x.ToList());

    public static async Task<List<T>> ReadExcelAsListAsync<T>(string path, string sheetName) => await ReadExcelAsync<T, List<T>>(path, sheetName, x => x.ToList());

    public static async Task<List<T>> ReadExcelAsListAsync<T>(string path, CultureInfo culture) => await ReadExcelAsync<T, List<T>>(path, culture, x => x.ToList());

    public static async Task<List<T>> ReadExcelAsListAsync<T>(string path, string sheetName, CultureInfo culture) => await ReadExcelAsync<T, List<T>>(path, sheetName, culture, x => x.ToList());

    public static async Task<List<T>> ReadExcelAsListAsync<T>(Stream stream, CultureInfo culture, bool leaveOpen = false) => await ReadExcelAsync<T, List<T>>(stream, culture, leaveOpen, x => x.ToList());

    public static async Task<List<T>> ReadExcelAsListAsync<T>(Stream stream, string sheetName, CultureInfo culture, bool leaveOpen = false) => await ReadExcelAsync<T, List<T>>(stream, sheetName, culture, leaveOpen, x => x.ToList());
    #endregion

    #region ReadExcelAsArrayAsync

    public static async Task<T[]> ReadExcelAsArrayAsync<T>(string path) => await ReadExcelAsync<T, T[]>(path, x => x.ToArray());

    public static async Task<T[]> ReadExcelAsArrayAsync<T>(string path, string sheetName) => await ReadExcelAsync<T, T[]>(path, sheetName, x => x.ToArray());

    public static async Task<T[]> ReadExcelAsArrayAsync<T>(string path, CultureInfo culture) => await ReadExcelAsync<T, T[]>(path, culture, x => x.ToArray());

    public static async Task<T[]> ReadExcelAsArrayAsync<T>(string path, string sheetName, CultureInfo culture) => await ReadExcelAsync<T, T[]>(path, sheetName, culture, x => x.ToArray());

    public static async Task<T[]> ReadExcelAsArrayAsync<T>(Stream stream, CultureInfo culture, bool leaveOpen = false) => await ReadExcelAsync<T, T[]>(stream, culture, leaveOpen, x => x.ToArray());

    public static async Task<T[]> ReadExcelAsArrayAsync<T>(Stream stream, string sheetName, CultureInfo culture, bool leaveOpen = false) => await ReadExcelAsync<T, T[]>(stream, sheetName, culture, leaveOpen, x => x.ToArray());

    #endregion

    #region ReadExcelAsync
    private static async Task<TReturn> ReadExcelAsync<TItem, TReturn>(string path, Func<IEnumerable<TItem>, TReturn> func) => await ReadExcelAsync(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix)), func);

    private static async Task<TReturn> ReadExcelAsync<TItem, TReturn>(string path, string sheetName, Func<IEnumerable<TItem>, TReturn> func) => await ReadExcelAsync(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix), sheetName), func);

    private static async Task<TReturn> ReadExcelAsync<TItem, TReturn>(string path, CultureInfo culture, Func<IEnumerable<TItem>, TReturn> func) => await ReadExcelAsync(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix), culture), func);

    private static async Task<TReturn> ReadExcelAsync<TItem, TReturn>(string path, string sheetName, CultureInfo culture, Func<IEnumerable<TItem>, TReturn> func) => await ReadExcelAsync(new ExcelParser(FileUtility.FormatePostfix(path, DefaultExcelPostfix), sheetName, culture), func);

    private static async Task<TReturn> ReadExcelAsync<TItem, TReturn>(Stream stream, CultureInfo culture, bool leaveOpen, Func<IEnumerable<TItem>, TReturn> func) => await ReadExcelAsync(new ExcelParser(stream, culture, leaveOpen), func);

    private static async Task<TReturn> ReadExcelAsync<TItem, TReturn>(Stream stream, string sheetName, CultureInfo culture, bool leaveOpen, Func<IEnumerable<TItem>, TReturn> func) => await ReadExcelAsync(new ExcelParser(stream, sheetName, culture, leaveOpen), func);

    private static async Task<TReturn> ReadExcelAsync<TItem, TReturn>(ExcelParser excelParser, Func<List<TItem>, TReturn> func)
    {
        using var reader = excelParser;
        using var csv = new CsvReader(reader);
        var list = new List<TItem>();
        await foreach (var item in csv.GetRecordsAsync<TItem>()) list.Add(item);
        return func(list);
    }
    #endregion

    #region WriteToExcel

    public static void WriteToExcel<T>(string path, IEnumerable<T> records) => WriteToExcel(records, () => new ExcelWriter(path));
    public static void WriteToExcel<T>(string path, IEnumerable<T> records, CultureInfo culture) => WriteToExcel(records, () => new ExcelWriter(path, culture));
    public static void WriteToExcel<T>(string path, IEnumerable<T> records, string sheetName) => WriteToExcel(records, () => new ExcelWriter(path, sheetName));
    public static void WriteToExcel<T>(string path, IEnumerable<T> records, string sheetName, CultureInfo culture) => WriteToExcel(records, () => new ExcelWriter(path, sheetName, culture));
    public static void WriteToExcel<T>(Stream stream, IEnumerable<T> records, CultureInfo culture, bool leaveOpen = false) => WriteToExcel(records, () => new ExcelWriter(stream, culture, leaveOpen));
    public static void WriteToExcel<T>(Stream stream, IEnumerable<T> records, string sheetName, CultureInfo culture, bool leaveOpen = false) => WriteToExcel(records, () => new ExcelWriter(stream, sheetName, culture, leaveOpen));

    private static void WriteToExcel<T>(IEnumerable<T> records, Func<ExcelWriter> func)
    {
        using var WriteToExcelr = func();
        WriteToExcelr.WriteRecords(records);
    }

    #endregion

    #region WriteToExcelAsync

    public static async Task WriteToExcelAsync<T>(string path, IEnumerable<T> records) => await WriteToExcelAsync(records, () => new ExcelWriter(path));
    public static async Task WriteToExcelAsync<T>(string path, IEnumerable<T> records, CultureInfo culture) => await WriteToExcelAsync(records, () => new ExcelWriter(path, culture));
    public static async Task WriteToExcelAsync<T>(string path, IEnumerable<T> records, string sheetName) => await WriteToExcelAsync(records, () => new ExcelWriter(path, sheetName));
    public static async Task WriteToExcelAsync<T>(string path, IEnumerable<T> records, string sheetName, CultureInfo culture) => await WriteToExcelAsync(records, () => new ExcelWriter(path, sheetName, culture));
    public static async Task WriteToExcelAsync<T>(Stream stream, IEnumerable<T> records, CultureInfo culture, bool leaveOpen = false) => await WriteToExcelAsync(records, () => new ExcelWriter(stream, culture, leaveOpen));
    public static async Task WriteToExcelAsync<T>(Stream stream, IEnumerable<T> records, string sheetName, CultureInfo culture, bool leaveOpen = false) => await WriteToExcelAsync(records, () => new ExcelWriter(stream, sheetName, culture, leaveOpen));

    private static async Task WriteToExcelAsync<T>(IEnumerable<T> records, Func<ExcelWriter> func)
    {
        using var WriteToExcelr = func();
        await WriteToExcelr.WriteRecordsAsync(records);
    }

    #endregion
}

public static partial class CsvHelperUtility
{
    /// <summary>
    /// 写入并打开
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="path"></param>
    /// <param name="records"></param>
    /// <returns></returns>
    public static async Task WriteToExcelAndOpenAsync<T>(string path, IEnumerable<T> records)
    {
        await WriteToExcelAsync(path, records);
        path.AsOpen();
    }
}